<template>
  <div class="b-tree" @scroll="handleScroll">
    <div class="b-tree__virtual" :style="{ height: contentHeight }"></div>
    <div class="b-tree__content">
      <div class="b-tree__list-view" v-for="(item, index) in visibleData" :key="item.id">
        <i
          :class="['b-tree__i', item.expand ? 'b-tree__expand' : 'b-tree__close']"
          v-if="item.children && item.children.length"
        ></i>
        <slot :item="item" :index="index"></slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "",
  props: {
    tree: {
      type: Array,
      required: true,
      default: () => []
    },
    defaultExpand: {
      type: Boolean,
      default: false
    },
    option: {
      // 配置对象
      type: Object,
      default: () => {}
    }
  },
  data() {
    return {
      offset: 0, // translateY偏移量
      visibleData: []
    };
  },
  computed: {
    contentHeight() {
      const heights =
        (this.flattenTree || []).filter(item => item.visible).length * this.option.itemHeight;
      return `${heights}px`;
    },
    flattenTree() {
      const flatten = (
        list,
        childKey = "children",
        level = 1,
        parent = null,
        defaultExpand = true
      ) => {
        let arr = [];
        list.forEach(item => {
          item.level = level;
          if (item.expand === "undefined") {
            item.expand = defaultExpand;
          }
          if (item.visible === "undefined") {
            item.visible = true;
          }
          if (!parent.visible || !parent.expand) {
            item.visible = false;
          }
          item.parent = parent;
          arr.push(item);
          if (item[childKey]) {
            //递归的添加元素
            arr.push(...flatten(item[childKey], childKey, level + 1, item, defaultExpand));
          }
        });
        return arr;
      };
      console.log(this.tree);
      return flatten(this.tree, "children", 1, {
        level: 0,
        visible: true,
        expand: true,
        children: this.tree
      });
    }
  },
  methods: {
    handleScroll(e) {
      const scrollTop = e.target.scrollTop;
      this.updateVisibleData(scrollTop);
    },
    updateVisibleData(scrollTop = 0) {
      const start = Math.floor(scrollTop / this.option.itemHeight);
      const end = start + this.option.visibleCount;
      const allVisibleData = (this.flattenTree || []).filter(item => item.visible);
      this.visibleData = allVisibleData.slice(start, end);
      this.offset = start * this.option.itemHeight;
    },
    //展开节点
    expand(item) {
      item.parent = true;
    },
    //折叠节点
    collapse(item) {
      item.parent = false;
    },
    //递归节点
    recursionVisible(children, status) {
      children.forEach(node => {
        node.visible = status;
        if (node.children) {
          //递归
          this.recursionVisible(node.children, status);
        }
      });
    }
  },
  mounted() {
    /**
    * 细节如下：
        1.整个容器使用相对定位是为了避免在滚动中引起页面回流
        2.phantom 容器为了撑开高度，让滚动条出现
        3.flattenTree 为了拍平 递归结构的tree数据，同时添加level、expand、visibel属性，分别代表节点层级、是否展开、是否可视
        4.contentHeight 动态计算容器的高度，隐藏（收起）节点不应该计算在总高度里面
        5.这样一来渲染大数据的tree组件就有了基本的雏形，接下来看看节点展开/收起如何实现
    */
    this.updateVisibleData();
  }
};
</script>

<style lang="stylus" scoped>
.b-tree {
  position: relative
  box-sizing: border-box
  max-height: 500px
  overflow-y: auto
  .b-tree__virtual {
    position: absolute
    top: 0
    left: 0
    bottom: 0
    z-index: -1
  }
  .b-tree__content {
    position: absolute
    top: 0
    left: 0
    bottom: 0
    min-height: 100px
  }
  .b-tree__list-view {
    display: flex
    align-items: center
    cursor: pointer
  }
  .b-tree__content__item {
    box-sizing: border-box
    padding: 5px
    display: flex
    justify-content: space-between
    align-items: center
    position: relative
    cursor: pointer
  }
  .b-tree__i {
    display: inline-block
    width: 0
    height: 0
    overflow: hidden
    margin-right: 5px
    border-width: 5px
  }
  .b-tree__expand {
    border-color: #C0C4CC transparent transparent transparent
    border-style: solid dashed dashed dashed
  }
  .b-tree__close {
    border-color: transparent transparent transparent #C0C4CC
    border-style: dashed dashed dashed solid
  }
  .b-tree__content__item:hover, .b-tree__content__item__selected {
    background-color: #d7d7d7
  }
}
</style>
